/*******************************************************************************
 * Copyright (c) 2000, 2015 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ui.internal;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;

import org.eclipse.jface.action.IMenuCreator;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MenuAdapter;
import org.eclipse.swt.events.MenuEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;

/**
 * The <code>NavigationHistoryAction</code> moves navigation history
 * back and forward.
 */
public class NavigationHistoryAction extends PageEventAction {

	private boolean recreateMenu;

	private boolean forward;

    private Menu historyMenu;

    private int MAX_HISTORY_LENGTH = 9;

    private class MenuCreator implements IMenuCreator {
        @Override
		public void dispose() {
        }

        @Override
		public Menu getMenu(Menu parent) {
        	setMenu(new Menu(parent));
        	fillMenu(historyMenu);
            initMenu();
            return historyMenu;
        }

        @Override
		public Menu getMenu(Control parent) {
        	setMenu(new Menu(parent));
        	fillMenu(historyMenu);
            initMenu();
            return historyMenu;
        }

    }

    private void setMenu(Menu menu) {
    	historyMenu = menu;
    }

    private void initMenu() {
    	historyMenu.addMenuListener(new MenuAdapter() {
    		@Override
			public void menuShown(MenuEvent e) {
    			if (recreateMenu) {
					Menu m = (Menu) e.widget;
					MenuItem[] items = m.getItems();
					for (MenuItem item : items) {
						item.dispose();
					}
					fillMenu(m);
				}
			}
    	});
    }

    private void fillMenu(Menu menu) {
    	IWorkbenchPage page = getWorkbenchWindow().getActivePage();
    	if (page == null) {
    		return;
    	}

    	final NavigationHistory history = (NavigationHistory) getWorkbenchWindow()
    	.getActivePage().getNavigationHistory();
    	NavigationHistoryEntry[] entries;
    	if (forward) {
    		entries = history.getForwardEntries();
    	} else {
    		entries = history.getBackwardEntries();
    	}
    	int entriesCount[] = new int[entries.length];
    	for (int i = 0; i < entriesCount.length; i++) {
    		entriesCount[i] = 1;
    	}
    	entries = collapseEntries(entries, entriesCount);
    	for (int i = 0; i < entries.length; i++) {
    		if (i > MAX_HISTORY_LENGTH) {
    			break;
    		}
    		String text = entries[i].getHistoryText();
    		if (text != null) {
    			MenuItem item = new MenuItem(menu, SWT.NONE);
    			item.setData(entries[i]);
    			if (entriesCount[i] > 1) {
					text = NLS.bind(WorkbenchMessages.get().NavigationHistoryAction_locations, text,
							Integer.valueOf(entriesCount[i]));
    			}
    			item.setText(text);
				item.addSelectionListener(new SelectionAdapter()
                {
				    public void widgetSelected(SelectionEvent e) {
				        history.shiftCurrentEntry((NavigationHistoryEntry) e.widget.getData(), forward);
				    }
                });
    		}
    	}
    	recreateMenu = false;
    }

    @Override
	public void dispose() {
    	super.dispose();
    	if (historyMenu != null) {
    		for (int i = 0; i < historyMenu.getItemCount(); i++) {
    			MenuItem menuItem = historyMenu.getItem(i);
    			menuItem.dispose();
    		}
    		historyMenu.dispose();
    		historyMenu = null;
    	}
    }

    /**
	 * Create a new instance of <code>NavigationHistoryAction</code>
	 *
	 * @param window
	 *            the workbench window this action applies to
	 * @param forward
	 *            if this action should move history forward of backward
	 */
    public NavigationHistoryAction(IWorkbenchWindow window, boolean forward) {
        super("", window); //$NON-NLS-1$
        ISharedImages sharedImages = window.getWorkbench().getSharedImages();
        if (forward) {
            setText(WorkbenchMessages.get().NavigationHistoryAction_forward_text);
            setToolTipText(WorkbenchMessages.get().NavigationHistoryAction_forward_toolTip);
            // @issue missing action id
            window.getWorkbench().getHelpSystem().setHelp(this,
					IWorkbenchHelpContextIds.NAVIGATION_HISTORY_FORWARD);
            setImageDescriptor(sharedImages
                    .getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD));
            setDisabledImageDescriptor(sharedImages
                    .getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD_DISABLED));
            setActionDefinitionId(IWorkbenchCommandConstants.NAVIGATE_FORWARD_HISTORY);
        } else {
            setText(WorkbenchMessages.get().NavigationHistoryAction_backward_text);
            setToolTipText(WorkbenchMessages.get().NavigationHistoryAction_backward_toolTip);
            // @issue missing action id
            window.getWorkbench().getHelpSystem().setHelp(this,
					IWorkbenchHelpContextIds.NAVIGATION_HISTORY_BACKWARD);
            setImageDescriptor(sharedImages
                    .getImageDescriptor(ISharedImages.IMG_TOOL_BACK));
            setDisabledImageDescriptor(sharedImages
                    .getImageDescriptor(ISharedImages.IMG_TOOL_BACK_DISABLED));
            setActionDefinitionId(IWorkbenchCommandConstants.NAVIGATE_BACKWARD_HISTORY);
        }
        // PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IHelpContextIds.CLOSE_ALL_PAGES_ACTION);
        setEnabled(false);
        this.forward = forward;
        setMenuCreator(new MenuCreator());
    }

    @Override
	public void pageClosed(IWorkbenchPage page) {
        super.pageClosed(page);
        setEnabled(false);
    }

    private NavigationHistoryEntry[] collapseEntries(
            NavigationHistoryEntry[] entries, int entriesCount[]) {
        ArrayList allEntries = new ArrayList(Arrays.asList(entries));
        NavigationHistoryEntry previousEntry = null;
        int i = -1;
        for (Iterator iter = allEntries.iterator(); iter.hasNext();) {
            NavigationHistoryEntry entry = (NavigationHistoryEntry) iter.next();
            if (previousEntry != null) {
                String text = previousEntry.getHistoryText();
                if (text != null) {
                    if (text.equals(entry.getHistoryText())
                            && previousEntry.editorInfo == entry.editorInfo) {
                        iter.remove();
                        entriesCount[i]++;
                        continue;
                    }
                }
            }
            previousEntry = entry;
            i++;
        }
        entries = new NavigationHistoryEntry[allEntries.size()];
        return (NavigationHistoryEntry[]) allEntries.toArray(entries);
    }

    @Override
	public void pageActivated(IWorkbenchPage page) {
        super.pageActivated(page);
        NavigationHistory nh = (NavigationHistory) page.getNavigationHistory();
        if (forward) {
            nh.setForwardAction(this);
        } else {
            nh.setBackwardAction(this);
        }
    }

    @Override
	public void run() {
        if (getWorkbenchWindow() == null) {
            // action has been disposed
            return;
        }
        IWorkbenchPage page = getActivePage();
        if (page != null) {
            NavigationHistory nh = (NavigationHistory) page
                    .getNavigationHistory();
            if (forward) {
                nh.forward();
            } else {
                nh.backward();
            }
            recreateMenu = true;
        }
    }

    public void update() {
        // Set the enabled state of the action and set the tool tip text.  The tool tip
        // text is set to reflect the item that one will move back/forward to.
        WorkbenchPage page = (WorkbenchPage) getActivePage();
        if (page == null) {
			return;
		}
        NavigationHistory history = (NavigationHistory) page
                .getNavigationHistory();
        NavigationHistoryEntry[] entries;
        if (forward) {
            setEnabled(history.canForward());
            entries = history.getForwardEntries();
            if (entries.length > 0) {
                NavigationHistoryEntry entry = entries[0];
                String text = NLS.bind(WorkbenchMessages.get().NavigationHistoryAction_forward_toolTipName, entry.getHistoryText() );
                setToolTipText(text);
            } else {
                setToolTipText(WorkbenchMessages.get().NavigationHistoryAction_forward_toolTip);
            }
        } else {
            setEnabled(history.canBackward());
            entries = history.getBackwardEntries();
            if (entries.length > 0) {
                NavigationHistoryEntry entry = entries[0];
                String text = NLS.bind(WorkbenchMessages.get().NavigationHistoryAction_backward_toolTipName, entry.getHistoryText() );
                setToolTipText(text);
            } else {
                setToolTipText(WorkbenchMessages.get().NavigationHistoryAction_backward_toolTip);
            }
        }
        recreateMenu = true;
    }
}
